/*
    <one line to give the library's name and an idea of what it does.>
    Copyright (C) 2013  Guillaume <email>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/


#ifndef MSSCALVARIABLE_H
#define MSSCALVARIABLE_H


#include <msTreeMapper.h>
#include <msUnitsManager.h>

namespace atomism
{

class msScalarVariable : public msTreeMapper
{
friend class msGeneralizedCoordinates;
      
     /** @Name From msRegister
     *///@{
      static msRegistrar Registrar;
      static bool  isScalarVariableRegisteredInPython;
      static msRegister* createInstance() { return new msScalarVariable; }
      //@}
      
protected:

      void registryInPython();  
      void initialize() 
            {
	     msTreeMapper::initialize();
             declareAttribute(Min,"Min"); declareAttribute(Default,"Default");
             declareAttribute(Max,"Max"); declareAttribute(dq,"dq");
             declareAttribute(Dq,"Dq");  
	     msTreeMapper::declareChild<msUnit>(Unit,msUnit::New(),"Unit");
             };
	     
     msScalarVariable() : msTreeMapper() { constructVar("msScalarVariable","ScalarVariable","scalar variable");
                                           Min=Max=dq=Dq=Value=0;
                                           }
public:
      
  //   enum typeVar{ length, angle};
     
     static boost::shared_ptr<msScalarVariable> New(string unit)  {

	       boost::shared_ptr<msScalarVariable> T( new msScalarVariable );
	       LOGGER_ENTER_FUNCTION_DBG("msScalarVariable::New(boost::shared_ptr<msScalarVariable> unit)",T->getFullId());
	       T->initialize(); 
	       try{ T->Unit->set( unit );
    		   }
	       catch(msError& e){ 
		    e.addContext("can not set the unit of the variable (static boost::shared_ptr<msScalarVariable> New(string unit))");
		    throw e;
		    }
	       T->update();
	       LOGGER_EXIT_FUNCTION();
	       return T;
	       }                    
    virtual boost::shared_ptr<msTreeMapper> clone() { 
            boost::shared_ptr<msScalarVariable> clone = New(""); 
	    clone->msTreeMapper::operator=(*this);					      
	    return clone;
            }  
     boost::shared_ptr<msTreeMapper> set(double v,double min,double max,double _dq,double _Dq)
                  {
		   return set(v, min, max, _dq, _Dq , Unit->getStr() ); 
		   }

     boost::shared_ptr<msTreeMapper> set(double v,double min,double max,double _dq,double _Dq, string unit)
                  {
 		   Min=min;Max=max;dq=_dq;Dq=_Dq;Value=v;Default=v;
                   
 		   try{ Unit->set( unit );
    		       }
		   catch(msError& e){ 
			 e.addContext("can not set the unit of the variable (boost::shared_ptr<msTreeMapper> set(double v,double min,double max,double _dq,double _Dq, string unit))");
			 throw e;
		         }
		   return mySharedPtr(); 
		   }
      
      double begin(){return Value=Min;}

      bool increment(){ Value+=Dq; if(Value>Max) return 0; return 1; }

      bool decrement(){ Value-=Dq; if(Value<Min) return 0; return 1; }

      bool isOutOfBounds() {if( (Value>Max)||(Value<Min)) return 1;return 0;}

      double value(double x) {return Value=Min+x*(Max-Min); }

      double getValue(const msUnitsManager& units) const	{ return units.convert(*Unit,Value); };
      double getMin(const msUnitsManager& units)   const	{ return units.convert(*Unit,Min);   };
      double getMax(const msUnitsManager& units)   const	{ return units.convert(*Unit,Max);   };
      double getDq(const msUnitsManager& units)    const	{ return units.convert(*Unit,Dq);    };
      double getdq(const msUnitsManager& units)    const	{ return units.convert(*Unit,dq);    };

      void setValue(double v, const msUnitsManager& units) 	{ Value = v / units.convert(*Unit,1); };
      void setMin(double v, const msUnitsManager& units)   	{ Min   = v / units.convert(*Unit,1); };
      void setMax(double v, const msUnitsManager& units)   	{ Max   = v / units.convert(*Unit,1); };
      void setDq(double v, const msUnitsManager& units)    	{ Dq    = v / units.convert(*Unit,1); };
      void setdq(double v, const msUnitsManager& units)    	{ dq    = v / units.convert(*Unit,1); };
    
      void setUnit(string unit){
          try{ Unit->set( unit );
          }
          catch(msError& e){
              e.addContext("can not set the unit of the variable (boost::shared_ptr<msTreeMapper> set(double v,double min,double max,double _dq,double _Dq, string unit))");
              throw e;
          }        
      }
      int indice(){ return (Value-Min)/Dq + 0.5;
		   };

      double operator()(){return Value;}

      std::string getUnitStr(){ return Unit->getStr(); }

      size_t noOfBins(){ return (Max-Min)/Dq; }

      double Default,Value,Min,Max,dq,Dq;
          
      bool isHomogeneousWith(std::string u) { return Unit->isConsistent( msUnit(u) ); }

      std::ostream& print(std::ostream& out) const;

      std::ostream& abstract(std::ostream& out) const;

protected:
   
      msChild<msUnit> Unit;

      };
      

  
} 

#endif // MSSCALVARIABLE_H
